import java.rmi.AccessException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.LinkedList;

import EnumerationPackage.EventID;
import EventPackage.Event;
import EventPackage.EventManagerInterface;
import EventPackage.EventQueue;
import InstrumentationPackage.MessageWindow;


public class ServMonitor extends Thread{
	private EventManagerInterface em = null;// Interface object to the event manager
	private String EvtMgrIP = null;			// Event Manager IP address

	boolean Registered = true;				// Signifies that this class is registered with an event manager.
	MessageWindow mw = null;				// This is the message window

	private long id;						//indicates participant id
	private int i;							//iterator
	private int deviceListSize;				//Holds the number of devices in the list
	private boolean devicePresent;			//A flag to indicate whether a device was already added to the list
	LinkedList<DeviceRecord> deviceList =  new LinkedList<DeviceRecord>(); //LinkedList of device record; class declaration at the bottom

	public ServMonitor()
	{
		// event manager is on the local system

		try
		{
			// Here we create an event manager interface object. This assumes
			// that the event manager is on the local machine

			em = new EventManagerInterface();
		}

		catch (Exception e)
		{
			System.out.println("SecMonitor::Error instantiating event manager interface: " + e);
			Registered = false;

		} // catch

	} //Constructor

	public ServMonitor( String EvmIpAddress )
	{
		// event manager is not on the local system

		EvtMgrIP = EvmIpAddress;

		try
		{
			// Here we create an event manager interface object. This assumes
			// that the event manager is NOT on the local machine

			em = new EventManagerInterface( EvtMgrIP );
		}

		catch (Exception e)
		{
			System.out.println("SecMonitor::Error instantiating event manager interface: " + e);
			Registered = false;

		} // catch

	} // Constructor

	public void run()
	{
		Event Evt = null;				// Event object
		EventQueue eq = null;			// Message Queue
		int EvtId = 0;					// User specified event ID
		int	Delay = 1000;				// The loop delay (1 second)
		boolean Done = false;			// Loop termination flag

		if (em != null)
		{
			// Now we create the security status, message panel and indicator


			mw = new MessageWindow("Service Maintenance Monitoring Console", 0, 0);

			mw.WriteMessage( "Registered with the event manager." );

			try
			{
				id = em.GetMyId();
				mw.WriteMessage("   Participant id: " + id);
				mw.WriteMessage("   Registration Time: " + em.GetRegistrationTime() );

			} // try

			catch (Exception e)
			{
				System.out.println("Error:: " + e);

			} // catch

			/********************************************************************
			 ** Here we start the main simulation loop
			 *********************************************************************/

			while ( !Done )
			{
				// Here we get our event queue from the event manager

				try
				{
					eq = em.GetEventQueue();

				} // try

				catch( Exception e )
				{
					mw.WriteMessage("Error getting event queue::" + e );

				} // catch

				// If there are messages in the queue, we read through them.
				// We are looking for EventIDs = 3 for security arm/disarm events.
				// Note that we get all the messages at once... there is a 1
				// second delay between samples,.. so the assumption is that there should
				// only be a message at most. If there are more, it is the last message
				// that will effect the status security controllers
				// as it would in reality.

				int qlen = eq.GetSize();

				for ( int i = 0; i < qlen; i++ )
				{
					Evt = eq.GetEvent();

					//sends a response based on service maintenance query
					if ( Evt.GetEventId() == EventID.SERV_QUERY.getEventIdNum() ){
						try {
							em.SendEvent(new Event(
									EventID.SERV_REPLY.getEventIdNum(), 
									id+ ",Service Maintenance Monitoring Console,monitors museum devices"));
						} catch (Exception e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}

					//reads responses based on service maintenance query
					else if ( Evt.GetEventId() == EventID.SERV_REPLY.getEventIdNum() ){
						//gets the comma separated reply in the event message
						String reply = Evt.GetMessage();
						devicePresent = false;
						deviceListSize = deviceList.size();
						i=0;
						
						while(i<deviceListSize){
							if(deviceList.get(i).isDevice(reply)){
								//the device is present in the list, break from the loop 
								devicePresent = true;
								break;
							}
							i++;
						}
						//if the device was not present in the list, add it
						if (devicePresent==false){
							deviceList.add(new DeviceRecord(reply));
						}

					}

					// If the event ID == 99 then this is a signal that the simulation
					// is to end. At this point, the loop termination flag is set to
					// true and this process unregisters from the event manager.

					else if ( Evt.GetEventId() == EventID.DONE.getEventIdNum() )
					{
						Done = true;

						try
						{
							em.UnRegister();

						} // try

						catch (Exception e)
						{
							mw.WriteMessage("Error unregistering: " + e);

						} // catch

						mw.WriteMessage( "\n\nSimulation Stopped. \n");

						// Get rid of the indicators. The message panel is left for the
						// user to exit so they can see the last message posted.

						//si.dispose();

					} // if

				} // for

				
				//iterate through the list of devices
				//find any devices that did not receive a response 
				int i =0;
				deviceListSize = deviceList.size();
				while (i<deviceListSize){
					deviceList.get(i);
					i++;
				}
				
				
				//sends a server query
				try {
					em.SendEvent(new Event(EventID.SERV_QUERY.getEventIdNum()));
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

				// This delay slows down the sample rate to Delay milliseconds

				try
				{
					Thread.sleep( Delay );

				} // try

				catch( Exception e )
				{
					System.out.println( "Sleep error:: " + e );

				} // catch

			} // while

		} else {

			System.out.println("Unable to register with the event manager.\n\n" );

		} // if

	} // main

	/***************************************************************************
	 * CONCRETE METHOD:: IsRegistered
	 * Purpose: This method returns the registered status
	 *
	 * Arguments: none
	 *
	 * Returns: boolean true if registered, false if not registered
	 *
	 * Exceptions: None
	 *
	 ***************************************************************************/

	public boolean IsRegistered()
	{
		return( Registered );

	} // SetTemperatureRange

	/***************************************************************************
	 * CONCRETE METHOD:: Halt
	 * Purpose: This method posts an event that stops the environmental control
	 *		   system.
	 *
	 * Arguments: none
	 *
	 * Returns: none
	 *
	 * Exceptions: Posting to event manager exception
	 *
	 ***************************************************************************/

	public void Halt()
	{
		mw.WriteMessage( "***HALT MESSAGE RECEIVED - SHUTTING DOWN SYSTEM***" );

		// Here we create the stop event.

		Event evt;

		evt = new Event( (int) 99, "XXX" );

		// Here we send the event to the event manager.

		try
		{
			em.SendEvent( evt );

		} // try

		catch (Exception e)
		{
			System.out.println("Error sending halt message:: " + e);

		} // catch

	} // Halt

	private class DeviceRecord{
		private long id;
		String type, description;
		int roundsMissing;
		boolean matchedThisRound;
		DeviceRecord(String reply){

			String[] split = reply.split(",");
			id = Long.parseLong(split[0]);
			type = split[1];
			description = split[3];
			roundsMissing = 0;
			matchedThisRound = true;
		}

		//returns true if the museum device reply string 
		//is the same as this device based on id
		boolean isDevice(String reply){
			String[] split = reply.split(",");
			if (id == Long.parseLong(split[0])){
				matchedThisRound = true;
				roundsMissing = 0;
				return true;
			}
			return false;
		}

		//returns true if the device has been missing for at least 5 rounds
		boolean isNotResponsive(){
			if (matchedThisRound == false){
				if (roundsMissing>=4){
					return true;
				}
				else {
					roundsMissing++;
				}
			}
			return false;
		}
	}
}
